
#include "common.h"
//#include "loggerData.h"
#include "eXosip2/eXosip.h"
#include "osip2/osip_time.h"
#include "osip2/osip_mt.h"
#include "osip2/osip_condv.h"
#include "osipparser2/osip_port.h"
#include <assert.h>
#include <stdlib.h>
#include <time.h>
#include <sstream>

#if defined(WIN32)
#include <winsock2.h>
#endif

using namespace std;


class LoggerData
{
public:
    LoggerData() {}
private:
    std::ostringstream oss_;
    common::omutex mutex_;
    friend class Logger::Guard;
};

LoggerData * Logger::instance()
{
    if (!mImpl)
    {
        mImpl = new LoggerData;
    }

    return mImpl;
}

LoggerData* Logger::mImpl = NULL;

Logger::Guard::Guard(const std::string& sub)
{
    instance()->oss_ << "[" << common::helper::str_time() << "][" << sub << "] - ";
}

Logger::Guard::~Guard()
{
    common::oscope_lock lock(instance()->mutex_);
    cout << mImpl->oss_.str() << endl;
    // clear
    instance()->oss_.str("");
    instance()->oss_.clear();
}

std::ostream& Logger::Guard::as_stream()
{
    return instance()->oss_;
}


//////////////////////////////////////////////////////////////////////////
ExosipAutoLock::ExosipAutoLock(eXosip_t* pCtx)
{
    mCtx = pCtx;
    //eXosip_lock(mCtx);
}

ExosipAutoLock::~ExosipAutoLock()
{
    //eXosip_unlock(mCtx);
}


namespace common
{
    //////////////////////////////////////////////////////////////////////////
    ostring::ostring() :osipcstr_(NULL)
    {
    }

    ostring::~ostring()
    {
        if(NULL != osipcstr_)
        {
            osip_free(osipcstr_);
            osipcstr_ = NULL;
        }
    }

    const bool ostring::empty() const
    {
        return (NULL == osipcstr_);
    }

    //////////////////////////////////////////////////////////////////////////
    omutex::omutex()
    {
        mutex_ = osip_mutex_init();
    }

    omutex::~omutex()
    {
        osip_mutex_destroy(mutex_);
    }

    bool omutex::lock()
    {
        return (osip_mutex_lock(mutex_) == 0) ? true : false;
    }

    void omutex::unlock()
    {
        osip_mutex_unlock(mutex_);
    }

    //////////////////////////////////////////////////////////////////////////
    oscope_lock::oscope_lock(omutex& mut ):mut_(mut)
    {
        mut_.lock();
    }

    oscope_lock::~oscope_lock()
    {
        mut_.unlock();
    }

    void oscope_lock::unlock()
    {
        mut_.unlock();
    }

    //////////////////////////////////////////////////////////////////////////
    osemaphore::osemaphore(unsigned int count /* = 0 */)
    {
        sem_ = osip_sem_init(count);
    }

    osemaphore::~osemaphore()
    {
        osip_sem_destroy(sem_);
    }

    bool osemaphore::post()
    {
        return (osip_sem_post(sem_)==0) ? true : false;
    }

    bool osemaphore::wait()
    {
        return (osip_sem_wait(sem_)==0) ? true : false;
    }

    bool osemaphore::trywait()
    {
        return (osip_sem_trywait(sem_)==0) ? true : false;
    }

    //////////////////////////////////////////////////////////////////////////
    ocondition::ocondition()
    {
        cond_ = osip_cond_init();
    }

    ocondition::~ocondition()
    {
        osip_cond_destroy(cond_);
    }

    bool ocondition::signal()
    {
        return (osip_cond_signal(cond_)==0) ? true : false;
    }

    bool ocondition::wait(omutex& mut, int tv_s /*= -1*/, int tv_ms /*= -1*/)
    {
        if(tv_s == -1 && tv_ms == -1)
        {
            return (osip_cond_wait(cond_, &mut)==0) ? true : false;
        }
        else
        {
            timespec ts;
            ts.tv_sec = tv_s;
            ts.tv_nsec = tv_ms * 1000000;
            return (osip_cond_timedwait(cond_, &mut, &ts)==0) ? true : false;
        }
    }

    //////////////////////////////////////////////////////////////////////////
    othread::othread() : handle_(NULL),bRunning_(false)
    {
    }

    othread::~othread()
    {
        assert(!bRunning_);
    }

    bool othread::start()
    {
        if(bRunning_)
            return true;
        handle_ = osip_thread_create(0, othread::_thread_entry_, this);
        return (NULL != handle_);
    }

    bool othread::join()
    {
        if (!bRunning_)
            return false;
        return (osip_thread_join(handle_) == 0) ? true : false;
    }

    void othread::stop()
    {
        if(bRunning_)
        {
            bRunning_ = false;
            join();
            osip_free(handle_);
            handle_ =  NULL;
        }
    }

    void* othread::_thread_entry_( void* param )
    {
        ((othread*)param)->thread_proc();
        osip_thread_exit();
        return NULL;
    }

    void othread::thread_proc()
    {
        if(startup())
        {
            bRunning_ = true;
            dowork();
            bRunning_ = false;
            cleanup();
        }
    }


    helper::NetworkInitializer::NetworkInitializer()
    {
#if defined(WIN32)
        mValid = false;

        WORD wVersionRequested = MAKEWORD(2, 2);
        WSADATA wsaData;
        int err;

        err = WSAStartup(wVersionRequested, &wsaData);
        if (err != 0)
        {
            // could not find a usable WinSock DLL
            //cerr << "Could not load winsock" << endl;
            //exit(1);
        }

        /* Confirm that the WinSock DLL supports 2.2.*/
        /* Note that if the DLL supports versions greater    */
        /* than 2.2 in addition to 2.2, it will still return */
        /* 2.2 in wVersion since that is the version we      */
        /* requested.                                        */

        if (LOBYTE(wsaData.wVersion) != 2 ||
            HIBYTE(wsaData.wVersion) != 2)
        {
            /* Tell the user that we could not find a usable */
            /* WinSock DLL.                                  */
            WSACleanup();
            //cerr << "Bad winsock verion" << endl;
            // TODO !cj! - add error message logging
            //exit(1);
        }
#endif
        mValid = true;
    }

    helper::NetworkInitializer::~NetworkInitializer()
    {
#if defined(WIN32)
        if (mValid) { WSACleanup(); }
#endif
    }

    helper::NetworkInitializer::operator bool() const
    {
        return mValid;
    }

    void helper::msleep(int millseconds)
    {
        osip_usleep(1000 * millseconds);
    }

    unsigned long helper::now_time()
    {
//         timeval now;
//         osip_gettimeofday(&now, NULL);
//         return 1000 * now.tv_sec + now.tv_usec / 1000;
         time_t t = time(NULL);
         return t;
    }

    const char* helper::str_time(time_t t /* = 0 */)
    {
        if (!t)
        {
            t = time(0);
        }
        static string ret;
        ret = asctime(localtime(&t));
        //trim line break
        ret.erase(ret.end() - 1, ret.end());

        return ret.c_str();
    }

    std::string helper::to_string(int val)
    {
        ostringstream oss;
        oss << val;
        return oss.str();
    }

    template<class T, class R>
    R helper::convert(T t)
    {
        R ret;
        return ret;
    }

} //namespace common
